import numpy as np
import pandas as pd
import random
from itertools import combinations
from operator import itemgetter
from ..models import Application, User, Mission
from flask import current_app


def get_record():
    user_list, mission_list, rating_list = list(), list(), list()
    app = Application.query.all()
    for app_item in app:
        score, applier_id, mission_id = app_item.score, app_item.applier_id, app_item.mission_id
        user_list.append(applier_id)
        mission_list.append(mission_id)
        if score is None:
            score = 2
        rating_list.append(score)

    record_num = len(user_list)
    data = np.zeros((record_num, 3), dtype=np.int32)
    data[:, 0] = user_list
    data[:, 1] = mission_list
    data[:, 2] = rating_list
    df = pd.DataFrame(data, columns=["userId", "itemId", "rating"])
    return df


def trans_df2dict(df):
    user_rating = dict()
    for row in df.values:
        userId, itemId, rating = row[0], row[1], row[2]
        if userId not in user_rating.keys():
            user_rating[userId] = {}
        user_rating[userId][itemId] = rating
    return user_rating


def get_items_similarity(df, movie_num):

    inverted_table = df.groupby(by='userId')['itemId'].agg(list).to_dict()
    W = np.zeros((movie_num, movie_num))

    count_item_users_num = df.groupby(
        by='itemId')['userId'].agg('count').to_dict()

    for key, val in inverted_table.items():
        val.sort(reverse=True)
        for per in combinations(val, 2):
            W[per[0] - 1][per[1] - 1] += 1
            W[per[1] - 1][per[0] - 1] += 1

    for i in range(W.shape[0]):
        for j in range(W.shape[1]):
            if count_item_users_num.get(i+1) is None or count_item_users_num.get(j + 1) is None:
                W[i][j] = 0
            else:
                W[i][j] /= np.sqrt(count_item_users_num.get(i + 1)
                                   * count_item_users_num.get(j + 1))

    w_dict = {}
    for i in range(W.shape[0]):
        tmp = []
        for index, k in enumerate(W[i]):
            tmp.append((index + 1, k))
        w_dict[i + 1] = tmp
    return w_dict


def user_interest_with_items(user_id, item_id, K, user_rating, w_dict):
    interest = 0
    # 按照和item_id的相似性降序排序
    for i in sorted(w_dict[item_id], key=itemgetter(1), reverse=True)[0:K]:
        item_index = i[0]
        item_simi = i[1]
        if item_index in user_rating[user_id].keys():
            interest += item_simi * user_rating[user_id][item_index]
    return interest


def get_user_interest_list(user_id, K, user_rating, w_dict):
    rank = []
    item_id_list = w_dict.keys()
    for item_id in item_id_list:
        if item_id in user_rating[user_id].keys():
            continue
        interest = user_interest_with_items(
            user_id, item_id, K, user_rating, w_dict)
        rank.append((item_id, interest))
    return sorted(rank, key=itemgetter(1), reverse=True)


def item_recommend(mission_type, keyword, user_id):
    # type强制搜索，keyword用推荐
    if mission_type or keyword:
        match_type_missions = []
        if mission_type:
            missions = Mission.query.filter(
                Mission.type == mission_type).all()
            match_type_missions = [
                mission_item.id for mission_item in missions]
        match_key_missions = []
        if keyword:
            fuzzy_matching = '%' + keyword + '%'
            missions_match_des = Mission.query.filter(
                Mission.description.like(fuzzy_matching)).all()

            missions_match_title = Mission.query.filter(
                Mission.title.like(fuzzy_matching)).all()

            match_key_missions = [
                item.id for item in missions_match_des + missions_match_title]
            current_app.logger.info("keyword: {}, len: {}".format(
                keyword, len(match_key_missions)))

        item_recommend_list = []
        if len(match_type_missions) > 0 and len(match_key_missions) > 0:
            item_recommend_list = list(
                set(match_type_missions) & set(match_key_missions))
        elif len(match_type_missions) > 0:
            item_recommend_list = list(set(match_type_missions))
        elif len(match_key_missions) > 0:
            item_recommend_list = list(set(match_key_missions))

    else:
        # df = trans_df() # 使用movieLens数据
        df = get_record()  # 使用果壳众包数据
        movie_num = df.itemId.nunique()
        movie_num = max(movie_num, df.itemId.max())
        user_num = df.userId.nunique()
        user_rating = trans_df2dict(df)
        w_dict = get_items_similarity(df, movie_num)
        if user_rating.get(user_id) is None:
            current_app.logger.info("here none")
            user = User.query_by_id(user_id)
            tag_list = user.tags
            if tag_list is None:
                mission = Mission.query.all()
                item_recommend_list = [
                    mission_item.id for mission_item in mission]
            else:
                item_recommend_list = list()
                user_tag_to_mission_type = {
                    '学科达人': ['学业探讨'], '科技发烧友': ['技术交流'], '热心公益': ['公益志愿'], '生活探索者': ['社会实践', '娱乐游玩']}
                for tag_item in tag_list:
                    missions_match_title = user_tag_to_mission_type.get(
                        tag_item)
                    if missions_match_title is None:    # tag为自定义 模糊匹配
                        type_fuzzy = '%' + tag_item + '%'
                        mission = Mission.query.filter(
                            Mission.type.like(type_fuzzy)).all()
                        item_recommend_list.extend(
                            [mission_item.id for mission_item in mission])
                    else:
                        for type_item in missions_match_title:
                            mission = Mission.query.filter(
                                Mission.type == type_item).all()
                            item_recommend_list.extend(
                                [mission_item.id for mission_item in mission])

                mission = Mission.query.all()
                mission_id_list = [mission_item.id for mission_item in mission]
                item_recommend_list.extend(
                    [i for i in mission_id_list if i not in item_recommend_list])
        else:
            current_app.logger.info("here not none")
            recommend_list = get_user_interest_list(
                user_id, 10, user_rating, w_dict)
            current_app.logger.info("interest list {}".format(recommend_list))
            item_recommend_list = [i[0] for i in recommend_list]
            mission = Mission.query.all()
            mission_id_list = [mission_item.id for mission_item in mission]
            item_recommend_list.extend(
                [i for i in mission_id_list if i not in item_recommend_list])

    # app = Application.query.filter(Application.applier_id==user_id).all()
    mission = Mission.query.filter(Mission.state > 1).all()
    # mission_have_down = [app_item.mission_id for app_item in app]
    mission_have_down = [mission_item.id for mission_item in mission]
    item_recommend_list = [
        i for i in item_recommend_list if i not in mission_have_down]
    # current_app.logger.info("推荐列表: {}".format(str(item_recommend_list)) )
    return item_recommend_list
